package cn.dong.ucenter.controller.api;

import cn.dong.api.domain.ResultCodeEnum;
import cn.dong.api.utils.ExceptionUtils;
import cn.dong.api.utils.HttpClientUtils;
import cn.dong.api.utils.JwtInfo;
import cn.dong.api.utils.JwtUtils;
import cn.dong.base.handler.CustomException;
import cn.dong.ucenter.domain.Member;
import cn.dong.ucenter.service.MemberService;
import cn.dong.ucenter.utils.WxProperties;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/api/ucenter/wx")
@Slf4j
public class ApiWxController {

    /**
     * 微信登录配置文件
     */
    @Autowired
    private WxProperties wxProperties;

    @Autowired
    private MemberService memberService;

    /**
     * 微信登录
     *
     * @return
     */
    @GetMapping("/login")
    public String wxLogin(HttpSession session) {
        /*
            完整的url：
                https://open.weixin.qq.com/connect/qrconnect
                ?appid=APPID
                &redirect_uri=REDIRECT_URI
                &response_type=code
                &scope=SCOPE
                &state=STATE
                #wechat_redirect
         */
        String baseUri = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=%s" +
                "&scope=%s" +
                "&state=%s" +
                "#wechat_redirect";

        // 应用唯一标识
        String appId = wxProperties.getAppId();

        // 请使用urlEncode对链接进行处理
        String encode = null;
        try {
            // redirect_uri需要将一些特殊字符转义，在浏览器的请求参数当中需要的
            encode = URLEncoder.encode(wxProperties.getRedirectUri(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(ExceptionUtils.getMessage(e));
            throw new CustomException(ResultCodeEnum.URL_ENCODE_ERROR);
        }
        String redirectUri = encode;

        // 填code
        String responseType = "code";
        // 应用授权作用域
        String scope = "snsapi_login";
        // 用于保持请求和回调的状态，使用uuid随机，防止csrf攻击（跨站请求伪造攻击）
        String state = UUID.fastUUID().toString();
        // 将uuid保存到redis缓存中，已经引入并启用spring-session-data-redis，会自动将session存入redis中
        session.setAttribute("guli-wx-state", state);

        // 字符串占位符拼接
        String url = String.format(baseUri, appId, redirectUri, responseType, scope, state);
        return "redirect:" + url;
    }

    /**
     * 微信登录回调
     *
     * @param code
     * @param state
     * @return
     */
    @GetMapping("/callback")
    public String callback(String code, String state, HttpSession session) {
        // ******************校验操作防止csrf攻击******************
        // 校验微信端返回过来的数据
        if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state)) {
            log.error("非法回调请求，用于保持请求和回调的状态错误！");
            throw new CustomException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

        // 校验自己随机的状态码，已经保存到redis中了，核对
        String sessionState = (String) session.getAttribute("guli-wx-state");
        if (!state.equals(sessionState)) {
            log.error("非法回调请求，微信登录回调参数为空！");
            throw new CustomException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }


        // ******************发送请求获取access_token和openId操作********************
        // 使用工具类发送httpclient请求：通过code获取access_token
        String baseUri = "https://api.weixin.qq.com/sns/oauth2/access_token";
        String appId = wxProperties.getAppId();
        String secret = wxProperties.getAppSecret();
        String grantType = "authorization_code";
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("appid", appId);
        map.put("secret", secret);
        map.put("code", code);
        map.put("grant_type", grantType);
        // 发送client请求
        HttpClientUtils clientUtils = new HttpClientUtils(baseUri, map);
        // 获取请求的内容
        String content = "";
        try {
            clientUtils.get();
            content = clientUtils.getContent();
        } catch (Exception e) {
            log.error("获取accessToken失败，" + e.getMessage());
            throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }
        log.info("微信登录：" + content);
        log.info("状态码：" + clientUtils.getStatusCode());
        // json解析获取的内容
        Map<String, Object> objectMap = (Map) JSON.parse(content);
        Integer errorStateCode = (Integer) objectMap.get("errcode");
        // 获取错误状态码，并判断，是否请求成功
        if (errorStateCode != null) {
            String stateInfo = (String) objectMap.get("errmsg");
            log.error("获取access_token，原因：" + stateInfo + "，状态码：" + errorStateCode);
            throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        // 提取json的内容openId，每个用户只有一个的id
        String openId = (String) objectMap.get("openid");
        // 提取access_token 接口调用凭证
        String accessToken = (String) objectMap.get("access_token");

        // *********************微信个人信息获取操作************************
        // 获取个人信息
        baseUri = "https://api.weixin.qq.com/sns/userinfo";
        // ?access_token=ACCESS_TOKEN&openid=OPENID
        try {
            map = new HashMap<String, String>();
            map.put("access_token", accessToken);
            map.put("openid", openId);
            clientUtils = new HttpClientUtils(baseUri, map);
            clientUtils.get();
            content = clientUtils.getContent();
        } catch (Exception e) {
            log.error("获取个人信息失败！，" + e.getMessage());
            throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }
        // 提取个人信息
        objectMap = (Map) JSON.parse(content);
        errorStateCode = (Integer) objectMap.get("errcode");
        // 获取错误状态码，并判断，是否请求成功
        if (errorStateCode != null) {
            String stateInfo = (String) objectMap.get("errmsg");
            log.error("获取access_token，原因：" + stateInfo + "，状态码：" + errorStateCode);
            throw new CustomException(ResultCodeEnum.FETCH_USERINFO_ERROR);
        }
        // 解析个人信息
        String nickname = (String) objectMap.get("nickname");
        Integer sex = (Integer) objectMap.get("sex");
        String privilege = (String) objectMap.get("headimgurl");



        // *********************数据库登录注册操作************************
        // jwt结果
        JwtInfo jwtInfo = null;

        // 数据操作
        QueryWrapper<Member> wrapper = new QueryWrapper<Member>();
        wrapper.eq("openid", openId);
        Member one = memberService.getOne(wrapper);
        // 判断
        if (one == null) {
            // 注册需要个人信息
            Member member = new Member();
            member.setAvatar(privilege);
            member.setSex(sex);
            member.setNickname(nickname);
            member.setOpenid(openId);
            memberService.save(member);
            // 封装jwt
            jwtInfo = new JwtInfo(member.getId(), nickname, privilege);
        } else {
            // 增加上一次的登录日期
            one.setGmtLogin(new Date());
            // 修改
            memberService.updateById(one);
            // 直接登录
            jwtInfo = new JwtInfo(one.getId(), one.getNickname(), one.getAvatar());
        }
        String jwtToken = JwtUtils.getJwtToken(jwtInfo, 3000);

        return "redirect:http://localhost:3000?token=" + jwtToken;
    }

}
